package com.njworkorder.HTSingleLogin;


import lombok.extern.slf4j.Slf4j;
import org.apereo.cas.client.session.SingleSignOutFilter;
import org.apereo.cas.client.util.HttpServletRequestWrapperFilter;
import org.apereo.cas.client.validation.Cas20ServiceTicketValidator;
import org.apereo.cas.client.validation.Cas30ServiceTicketValidator;
import org.apereo.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.CorsFilter;

import java.util.*;

/**
 * @author ht
 * @since 2024/02/04 10:55
 */
@Configuration
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@Slf4j
public class SecurityConfig {


    @Value("${cas.server-url-prefix}")
    private String serverUrlPrefix;
    @Value("${cas.server-login-url}")
    private String serverLoginUrl;
    @Value("${cas.client-host-url}")
    private String serviceHostUrl;
    @Value("${cas.ignore-host-url}")
    private String ignoreHostUrl;
    @Value("${cas.client-redirect-url}")
    private String redirectUrl;

    @Value("${cas.enable}")
    private Boolean casEnable;

    private final TokenAuthenticationFilter tokenAuthenticationFilter;
    private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    private final UserDetailsService casUserDetailsService;
    private final UserDetailsService userDetailsService;
    private final CorsFilter corsFilter;
    private final CasAuthenticationSuccessHandler casAuthenticationSuccessHandler;
    /**
     * 认证失败处理类
     */
    private final AuthenticationEntryPointImpl unauthorizedHandler;

    public SecurityConfig(TokenAuthenticationFilter tokenAuthenticationFilter,
                          JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter,
                          @Qualifier("UserDetailsServiceImpl") UserDetailsService userDetailsService,
                          @Qualifier("CasUserDetailsServiceImpl") UserDetailsService casUserDetailsService,
                          CorsFilter corsFilter,AuthenticationEntryPointImpl unauthorizedHandler,
                          CasAuthenticationSuccessHandler casAuthenticationSuccessHandler){
        this.tokenAuthenticationFilter = tokenAuthenticationFilter;
        this.jwtAuthenticationTokenFilter = jwtAuthenticationTokenFilter;
        this.userDetailsService = userDetailsService;
        this.casUserDetailsService = casUserDetailsService;
        this.corsFilter = corsFilter;
        this.casAuthenticationSuccessHandler = casAuthenticationSuccessHandler;
        this.unauthorizedHandler = unauthorizedHandler;
    }

    //@Autowired
    //private LogoutSuccessHandler logoutSuccessHandler;

    private final AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/cas-api");

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManager() {

        log.info("casUserDetailsService===={}",casUserDetailsService);
        log.info("userDetailsService===={}",userDetailsService);

        List<AuthenticationProvider> authenticationProviders = new ArrayList<>();

            //本地登录
            DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
            // 身份认证接口
            daoAuthenticationProvider.setUserDetailsService(userDetailsService);
            daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
            if (casEnable)
            {
                //cas登录
                CasAuthenticationProvider casAuthenticationProvider = casAuthenticationProvider(casUserDetailsService);
                authenticationProviders.add(casAuthenticationProvider);
            }
            authenticationProviders.add(daoAuthenticationProvider);
            return new ProviderManager(authenticationProviders);
    }




    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.csrf(AbstractHttpConfigurer::disable);
        http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.authorizeHttpRequests((authorize) -> {
            authorize
                    .requestMatchers(new String[]{
                    "/login",
                    "/register",
                    "/sendSms",
                    "/",
                    "/*.html",
                    "/**.html",
                    "/**.css",
                    "/**.js",
                    "/profile/**",
                    "/files/**",
                    }).permitAll()
                    .requestMatchers(new String[]{
                    "/captchaImage",
                    "/prod-api/cas-api/**",
                    "/cas-api",
                    "/test",
                    "/flowable/**",
                    "/socket/**",
                    "/swagger-ui.html",
                    "/swagger-ui/**",
                    "/swagger-resources/**",
                    "/webjars/**",
                    "/*/api-docs",
                    "/*/api-docs/**",
                    "/doc.html",
                    "/plan/getPlanList",
                    "/diseaseRegistration/qsyhxcOpenapiGetDisInfoTest",
                    }).permitAll()
                    .requestMatchers(ignoreHostUrl.split(";")).permitAll().anyRequest().authenticated();
        });
            // 认证入口
            http.exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(casAuthenticationEntryPoint()));

            http.addFilterBefore(tokenAuthenticationFilter, CasAuthenticationFilter.class);
            // 添加CAS过滤器
            http.addFilterBefore(new SingleSignOutFilter(), CasAuthenticationFilter.class);
            // 添加JWT filter
            http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
            // 添加CORS filter
            http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
            http.addFilterBefore(corsFilter, LogoutFilter.class);
            http.addFilter(casAuthenticationFilter());
            http.addFilterAfter(new HttpServletRequestWrapperFilter(), CasAuthenticationFilter.class);
            http.addFilterAfter(new MyLogFilter(), HttpServletRequestWrapperFilter.class);

            // 注销登录
            http.headers((headersCustomizer) -> {
                headersCustomizer.cacheControl(HeadersConfigurer.CacheControlConfig::disable).frameOptions(options -> options.sameOrigin());
            });
            http.logout((logout) -> {
                logout.logoutSuccessUrl("/logout");
                //logout.logoutSuccessHandler(logoutSuccessHandler);
            });

        return http.build();
    }

    /**
     * CAS认证提供者
     */
    public CasAuthenticationProvider casAuthenticationProvider(UserDetailsService userDetailsService) {
        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setServiceProperties(serviceProperties());
        provider.setTicketValidator(cas20ServiceTicketValidator());
        provider.setKey("key");
        return provider;
    }

    /**
     * 当前CAS服务器不支持30版本的认证
     * CAS30 Ticket 校验器
     */
    private TicketValidator cas30ServiceTicketValidator() {
        return new Cas30ServiceTicketValidator(this.serverUrlPrefix);
    }

    /**
     * CAS20 Ticket 校验器
     */
    public TicketValidator cas20ServiceTicketValidator() {
        return new Cas20ServiceTicketValidator(this.serverUrlPrefix);
    }

    /**
     * 认证入口，跳转到CAS登录页
     */
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
        //需要进行判断  todo 当前的登录方式
        casAuthenticationEntryPoint.setLoginUrl(this.serverLoginUrl);
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
        return casAuthenticationEntryPoint;
    }

    /**
     * CAS认证过滤器
     */
    public CasAuthenticationFilter casAuthenticationFilter() {
        log.info("cas认证过滤器开始");
        CasAuthenticationFilter filter = new CasAuthenticationFilter();
//        CasAuthenticationProvider casAuthenticationProvider = casAuthenticationProvider(userDetailsService);
        // 设置需要使用CAS认证的请求url信息，只有匹配为true才进入CAS认证，否则忽略
        filter.setRequiresAuthenticationRequestMatcher(getRequestMatcher());
//        filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(redirectUrl));
        //认证成功回调前端
        log.info("cas认证成功处理器{}",casAuthenticationSuccessHandler);
        filter.setAuthenticationSuccessHandler(casAuthenticationSuccessHandler);
        filter.setAuthenticationManager(authenticationManager());
        log.info("cas认证过滤器结束");
        return filter;
    }

    /**
     * 客户端service 参数
     */
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        // service的地址为：${客户端的主机地址} + ":" + ${server.servlet.context-path} + "/login/cas" 否则无法换取票据, 会重复重定向
        serviceProperties.setService(serviceHostUrl);
        return serviceProperties;
    }

    private RequestMatcher getRequestMatcher() {
        return request -> {
            log.info("cas认证路径{}",request.getRequestURI());
            if (!antPathRequestMatcher.matches(request)) {
                log.info("cas-api=====测试{}",request.getRequestURI());
                return false;
            }
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken)) {
                log.info("requestId[{}] already authenticated, token is: {}", request.getRequestedSessionId(), authentication.getName());
                return false;
            }
            log.info("requestId[{}] not authenticated, need to do it", request.getRequestedSessionId());
            return true;
        };
    }


    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        SimpleUrlLogoutSuccessHandler successHandler = new SimpleUrlLogoutSuccessHandler();
        successHandler.setDefaultTargetUrl("/login?logout");
        return successHandler;
    }



}
